import struct
import ctypes
import pylynchipsdk as sdk
from common.python.plugin_utils import *
from common.python.infer_process import *
from common.python.callback_data_struct import *
from ctypes import *
from ipe_param_track import *


def return_buffer(callback_data) -> int:
    """将buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffer]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].push(callback_data[1])
    return 0


def free_device_memory(mem) -> int:
    """释放lyn_malloc申请的内存

    Args:
        mem (): device侧的内存地址

    Returns:
        _type_: integer，默认为0
    """
    sdk.lyn_free(mem)
    return 0


def return_buffers(callback_data) -> int:
    """将列表中的buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffers]，第二个元素为buffer组成的list

    Returns:
        _type_: integer，默认为0
    """
    for buffer in callback_data[1]:
        callback_data[0].push(buffer)
    return 0


def put_to_queue(callback_data) -> int:
    """将元素放入queue中

    Args:
        callback_data (list): 大小为2的list：[queue, element]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].put(callback_data[1])
    return 0


def get_rect(bbox_instance, width, height) -> Tuple[Tuple[int, int, int, int], bool]:
    """从Box实例中获取合法的矩形的大小和位置信息

    Args:
        bbox_instance (Box): Box实例
        width (integer): 视频分辨率的宽度
        height (integer):  视频分辨率的长度

    Returns:
        Tuple[Tuple[int, int, int, int], bool]: x,y,w,h和是否合法
    """
    x = bbox_instance.xmin
    y = bbox_instance.ymin
    w = bbox_instance.xmax - bbox_instance.xmin
    h = bbox_instance.ymax - bbox_instance.ymin
    # 确保矩形的x，y在视频长宽的范围内
    x = max(0, x)
    y = max(0, y)
    x = min(width - 8 - 1, x)
    y = min(height - 8 - 1, y)
    w = min(w, width - x - 1)
    h = min(h, height - y - 1)
    is_valid = w >= 8 and h >= 8

    # 确保x,y,w,h都是偶数
    def ensure_even(n):
        return n + 1 if n % 2 != 0 else n

    result_tuple = tuple(ensure_even(n) for n in (x, y, w, h))
    return result_tuple, is_valid


class Encoder:
    """视频编码类"""

    def __init__(self, ctx, framepool, vdec_out_info, output_path):
        self.frame_pool = framepool
        sdk.lyn_set_current_context(ctx)
        self.venc_recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.venc_send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.enc_head_flag = True
        venc_attr = sdk.lyn_venc_attr_t()
        ret = sdk.lyn_venc_set_default_params(venc_attr)
        common.error_check(ret, "lyn_venc_set_default_params")
        venc_attr.codec_type = sdk.lyn_codec_id_t.LYN_CODEC_ID_H264
        self.vdec_out_info = vdec_out_info
        venc_attr.width = self.vdec_out_info.width
        venc_attr.height = self.vdec_out_info.height
        venc_attr.bit_depth = 8
        venc_attr.bframes_num = 0
        venc_attr.pframes_num = 5
        venc_attr.input_format = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        venc_attr.target_bitrate = 6000000
        venc_attr.level = -1
        self.venc_handle, ret = sdk.lyn_venc_open(venc_attr)
        common.error_check(ret, "lyn_venc_open")
        self.output_path = output_path
        self.venc_recv_pool = bufferpool.buffer_pool(
            self.vdec_out_info.predict_buf_size, 5
        )

    def encode(
        self,
        frame,
    ):
        if self.enc_head_flag:
            self.enc_head_flag = False
            enc_packet = sdk.lyn_packet_t()
            enc_packet.size = self.vdec_out_info.predict_buf_size
            enc_packet.data = self.venc_recv_pool.pop()
            encode_data = save_file_cb_data()
            encode_data.output_path = self.output_path
            encode_data.packet = enc_packet
            encode_data.recv_pool = self.venc_recv_pool
            encode_data.file_path = self.output_path
            ret = sdk.lyn_venc_get_paramsset_async(
                self.venc_recv_stream, self.venc_handle, enc_packet
            )
            common.error_check(ret, "lyn_venc_get_paramsset_async")
            ret = sdk.lyn_stream_add_callback(
                self.venc_recv_stream, save_file_cb, encode_data
            )
            common.error_check(ret, "lyn_stream_add_callback")

        ret = sdk.lyn_venc_sendframe_async(
            self.venc_send_stream, self.venc_handle, frame
        )
        common.error_check(ret, "lyn_venc_sendframe_async")
        enc_packet = sdk.lyn_packet_t()
        enc_packet.size = self.vdec_out_info.predict_buf_size
        enc_packet.eos = frame.eos
        enc_packet.data = self.venc_recv_pool.pop()
        encode_data = save_file_cb_data()
        encode_data.packet = enc_packet
        encode_data.recv_pool = self.venc_recv_pool
        encode_data.file_path = self.output_path
        encode_data.output_path = self.output_path
        ret = sdk.lyn_stream_add_callback(
            self.venc_send_stream,
            free_to_pool_callback,
            [self.frame_pool, frame.data],
        )
        common.error_check(ret, "lyn_stream_add_callback")
        ret = sdk.lyn_venc_recvpacket_async(
            self.venc_recv_stream, self.venc_handle, enc_packet
        )
        common.error_check(ret, "lyn_venc_recvpacket_async")
        ret = sdk.lyn_stream_add_callback(
            self.venc_recv_stream, save_file_cb, encode_data
        )
        common.error_check(ret, "lyn_stream_add_callback")

    def __del__(self):
        sdk.lyn_synchronize_stream(self.venc_recv_stream)
        sdk.lyn_synchronize_stream(self.venc_send_stream)
        sdk.lyn_destroy_stream(self.venc_recv_stream)
        sdk.lyn_destroy_stream(self.venc_send_stream)
        sdk.lyn_venc_close(self.venc_handle)
        


class OpencvWindow:
    """opencv窗口类"""

    def __init__(self, frame_pool, video_frame, vdec_out_info):
        self.frame_pool = frame_pool
        self.video_frame = video_frame
        self.vdec_out_info = vdec_out_info

    def show(self, frame):
        data = np.empty(frame.size, dtype=np.uint8)
        data_ptr = sdk.lyn_numpy_to_ptr(data)
        ret = sdk.lyn_memcpy(
            data_ptr,
            frame.data,
            frame.size,
            sdk.lyn_memcpy_dir_t.ServerToClient,
        )
        common.error_check(ret, "lyn_memcpy")
        yuvImg = np.reshape(
            data, (self.vdec_out_info.height * 3 // 2, self.vdec_out_info.width)
        ).astype(np.uint8)
        if frame.eos:
            self.video_frame.put([yuvImg, frame.eos])
        else:
            self.video_frame.queue.clear()
            self.video_frame.put([yuvImg, frame.eos])
        self.frame_pool.push(frame.data)
        return 0


class ObjectTrack:

    def __init__(self):
        self.device_id = 0
        self.channel_id = 0
        self.model_path = ""
        self.show_type = 1
        self.video_frame = ""
        self.input_file = ""
        self.output_path = ""
        self.post_plugin_path1 = ""
        self.post_plugin_path2 = ""
        self.detect_model_path = ""
        self.track_model_path = ""
        self.osd_plugin = ""
        self.__demux_hdl = ""
        self.__vdec_hdl = ""
        self.MAX_BATCH = 20

    def init(self):
        self.__ctx, ret = sdk.lyn_create_context(self.device_id)
        self.use_local_input = os.path.exists(self.input_file)
        common.error_check(ret, "lyn_create_context")

        self.label_list, self.label_info_dev = create_label_list_with_names(
            self.label_file
        )

        self.init_demuxer()
        self.init_decoder()
        self.create_queue()
        self.register_plugin()

    def init_decoder(self):
        sdk.lyn_set_current_context(self.__ctx)
        self.__vdec_attr = sdk.lyn_vdec_attr_t()
        self.__vdec_attr.codec_id = self.codec_para.codec_id
        self.__vdec_attr.output_fmt = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        self.__vdec_attr.scale = sdk.lyn_scale_t.SCALE_NONE
        self.__vdec_hdl, ret = sdk.lyn_vdec_open(self.__vdec_attr)
        common.error_check(ret, "lyn_vdec_open")
        self.vdec_out_info, ret = sdk.lyn_vdec_get_out_info(
            self.codec_para, self.__vdec_attr
        )
        common.error_check(ret, "lyn_vdec_get_out_info")
        self.frame_pool = bufferpool.buffer_pool(self.vdec_out_info.predict_buf_size, 5)

    def init_demuxer(self):
        self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
        common.error_check(ret, "lyn_demux_open")
        self.codec_para, ret = sdk.lyn_demux_get_codec_para(self.__demux_hdl)
        common.error_check(ret, "lyn_demux_get_codec_para")

    def create_queue(self):
        self.__send_queue = block_queue()
        self.__recv_queue = block_queue()
        self.__detect_result_queue = block_queue()
        self.__recognize_result_queue = block_queue()

    def register_plugin(self):
        self.plugin1, ret = sdk.lyn_plugin_register(self.post_plugin_path1)
        common.error_check(ret, "lyn_plugin_register")
        self.plugin2, ret = sdk.lyn_plugin_register(self.post_plugin_path2)
        common.error_check(ret, "lyn_plugin_register")
        self.osd_plugin, ret = sdk.lyn_plugin_register(self.osd_plugin)
        common.error_check(ret, "lyn_plugin_register")

    def run(self, cancel_flag):
        self.__decode_send_thread = threading.Thread(
            target=self.decoder_send, args=(cancel_flag,)
        )
        self.__decode_send_thread.start()

        # 开启接收线程
        self.__decode_recv_thread = threading.Thread(
            target=self.decoder_recv, args=(cancel_flag,)
        )
        self.__decode_recv_thread.start()

        # 开启检测线程
        self.__detect_thread = threading.Thread(target=self.detect, args=(cancel_flag,))
        self.__detect_thread.start()

        # 开启跟踪线程
        self.__track_thread = threading.Thread(target=self.track, args=(cancel_flag,))
        self.__track_thread.start()

        # 开启展示或保存线程
        self.__generate_result_thread = threading.Thread(
            target=self.generate_result, args=(cancel_flag,)
        )
        self.__generate_result_thread.start()

    def decoder_send(self, cancel_flag):
        # 设置上下文环境 创建发送stream
        sdk.lyn_set_current_context(self.__ctx)
        send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            # 从解封装器读取一个包
            pkt, ret = sdk.lyn_demux_read_packet(self.__demux_hdl)
            # common.error_check(ret, "lyn_demux_read_packet")
            eos = pkt.eos
            reconnect = ret == 101003
            if reconnect:
                eos = False
                self.__send_queue.put((eos, reconnect))
                ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
                self.__decode_recv_thread.join()
                self.__generate_result_thread.join()
                self.frame_pool.free_buffers()
                sdk.lyn_vdec_close(self.__vdec_hdl)
                sdk.lyn_demux_close(self.__demux_hdl)

                self.init_demuxer()
                self.init_decoder()
                print(
                    f"cnannel {self.device_id}_{self.channel_id} changed resolution to: {self.vdec_out_info.width}, {self.vdec_out_info.height}"
                )

                self.__decode_recv_thread = threading.Thread(
                    target=self.decoder_recv, args=(cancel_flag,)
                )
                self.__decode_recv_thread.start()
                self.__generate_result_thread = threading.Thread(
                    target=self.generate_result, args=(cancel_flag,)
                )
                self.__generate_result_thread.start()
                continue

            if (eos or ret == lynEEOF) and not self.use_local_input:
                sdk.lyn_demux_close(self.__demux_hdl)
                time.sleep(500.0 / 1000)
                print("demux failed, reconnecting...")
                self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
                common.error_check(ret, "lyn_demux_open")
                eos = False
                continue

            # 发送给解码器解码
            ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
            common.error_check(ret, "lyn_vdec_send_packet_async")
            ret = sdk.lyn_synchronize_stream(send_stream)
            common.error_check(ret, "lyn_synchronize_stream")
            # 释放packet内存并通知接收结果
            if not eos:
                sdk.lyn_demux_free_packet(pkt)
            self.__send_queue.put((eos, reconnect))
            if cancel_flag.value:
                return

    def decoder_recv(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            eos, reconnect = self.__send_queue.take()
            if reconnect:
                cb_data = recv_cb_data()
                cb_data.reconnect = reconnect
                ret = sdk.lyn_stream_add_callback(
                    recv_stream, put_to_queue, [self.__recv_queue, cb_data]
                )
                self.__send_queue.clear()
                break
            cb_data = recv_cb_data()
            cb_data.frame.eos = eos
            cb_data.frame.data = self.frame_pool.pop()
            cb_data.frame.size = self.vdec_out_info.predict_buf_size
            cb_data.frame_pool = self.frame_pool
            cb_data.block_queue = self.__recv_queue
            cb_data.video_frame = self.video_frame
            ret = sdk.lyn_vdec_recv_frame_async(
                recv_stream, self.__vdec_hdl, cb_data.frame
            )
            common.error_check(ret, "lyn_vdec_recv_frame_async")
            ret = sdk.lyn_stream_add_callback(
                recv_stream, put_to_queue, [self.__recv_queue, cb_data]
            )
            common.error_check(ret, "lyn_stream_add_callback")
            if cancel_flag.value:
                return

    def detect_ipe(
        self,
        ipe_stream,
        frame,
        ipe_out_data,
        model_width,
        model_height,
        ipe_input_desc,
        ipe_output_desc,
        ipe_config,
    ):
        # ipe_input_desc, ret = sdk.lyn_ipe_create_pic_desc()
        # common.error_check(ret, "lyn_ipe_create_pic_desc")
        # ipe_output_desc, ret = sdk.lyn_ipe_create_pic_desc()
        # common.error_check(ret, "lyn_ipe_create_pic_desc")
        # ipe_config, ret = sdk.lyn_ipe_create_config_desc()
        # common.error_check(ret, "lyn_ipe_create_config_desc")

        sdk.lyn_set_current_context(self.__ctx)
        (
            resize_width,
            resize_height,
            pad_x,
            pad_y,
        ) = set_padding_data(
            self.vdec_out_info.width,
            self.vdec_out_info.height,
            model_width,
            model_height,
        )
        # 设置ipe输入
        ret = sdk.lyn_ipe_set_input_pic_desc(
            ipe_input_desc,
            frame.data,
            self.vdec_out_info.width,
            self.vdec_out_info.height,
            self.__vdec_attr.output_fmt,
        )
        common.error_check(ret, "lyn_ipe_set_input_pic_desc")
        ret = sdk.lyn_ipe_set_output_pic_data(ipe_output_desc, ipe_out_data)
        common.error_check(ret, "lyn_ipe_set_output_pic_data")
        ret = sdk.lyn_ipe_set_resize_config(ipe_config, resize_width, resize_height)
        common.error_check(ret, "lyn_ipe_set_resize_config")
        ret = sdk.lyn_ipe_set_pad_config(
            ipe_config,
            pad_y,
            pad_x,
            pad_y,
            pad_x,
            114,
            114,
            114,
        )
        common.error_check(ret, "lyn_ipe_set_pad_config")
        ret = sdk.lyn_ipe_set_c2c_config(
            ipe_config, sdk.lyn_pixel_format_t.LYN_PIX_FMT_RGB24, 0
        )
        common.error_check(ret, "lyn_ipe_set_c2c_config")
        ret = sdk.lyn_ipe_cal_output_pic_desc(
            ipe_output_desc, ipe_input_desc, ipe_config, 0
        )
        common.error_check(ret, "lyn_ipe_cal_output_pic_desc")
        ret = sdk.lyn_ipe_process_async(
            ipe_stream, ipe_input_desc, ipe_output_desc, ipe_config
        )
        common.error_check(ret, "lyn_ipe_process_async")

    def detect_callback(self, callback_data):
        """将后处理结果复制回来，将frame和box信息放入检测结果队列"""
        dst_img_size = ctypes.sizeof(Box)
        host_buf_arr = np.zeros(dst_img_size, dtype=np.uint8)
        host_buf = sdk.lyn_numpy_to_ptr(host_buf_arr)
        ret = sdk.lyn_memcpy(
            host_buf,
            callback_data[1],
            dst_img_size,
            sdk.lyn_memcpy_dir_t.ServerToClient,
        )
        common.error_check(ret, "detect_callback lyn_memcpy")
        df = detect_frame()
        df.frame = callback_data[0]
        df.device_box_info = callback_data[1]
        df.host_box_info = host_buf_arr
        self.__detect_result_queue.put(df)
        return 0

    def detect(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        plugin_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        post_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        plugin_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        detect_model, ret = sdk.lyn_load_model(self.detect_model_path)
        common.error_check(ret, "lyn_load_model")
        # 获取模型信息
        model_desc, ret = sdk.lyn_model_get_desc(detect_model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = (
            model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        )
        apu_output_size = model_desc.outputDataLen
        apu_buffer_pool = bufferpool.buffer_pool(apu_output_size * batch_size, 5)
        ipe_buffer_pool = bufferpool.buffer_pool(ipe_output_size * batch_size, 5)
        ipe_detect = IpeParamTrack(model_width, model_height)
        ipe_detect.set_img_info(
            self.codec_para.width,
            self.codec_para.height,
            sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
        )

        eos = False
        while not eos:
            cb_data = self.__recv_queue.take()
            if cb_data.reconnect:
                df = detect_frame()
                df.reconnect = cb_data.reconnect
                sdk.lyn_stream_add_callback(
                    post_stream, put_to_queue, [self.__detect_result_queue, df]
                )
                self.__recv_queue.clear()
                continue
            eos = cb_data.frame.eos
            ipe_buffer = ipe_buffer_pool.pop()
            apu_buffer = apu_buffer_pool.pop()
            # self.detect_ipe(
            #     ipe_stream, cb_data.frame, ipe_buffer, model_width, model_height,
            #     ipe_input_desc, ipe_output_desc, ipe_config
            # )
            ipe_detect.calc_param(ipe_stream, cb_data.frame.data, ipe_buffer)
            sdk.lyn_record_event(ipe_stream, ipe_event)
            sdk.lyn_stream_wait_event(apu_stream, ipe_event)

            sdk.lyn_execute_model_async(
                apu_stream, detect_model, ipe_buffer, apu_buffer, batch_size
            )
            sdk.lyn_record_event(apu_stream, apu_event)
            sdk.lyn_stream_wait_event(plugin_stream, apu_event)
            pythonapi.PyCapsule_GetPointer.restype = c_void_p
            pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]
            boxes_info, ret = sdk.lyn_malloc(ctypes.sizeof(Box))
            common.error_check(ret, "lyn_malloc")
            apu_data_ptr = pythonapi.PyCapsule_GetPointer(apu_buffer, None)
            boxes_info_ptr = pythonapi.PyCapsule_GetPointer(boxes_info, None)

            post_para = YoloxPostProcessInfo_t()
            post_para.height = model_height
            post_para.width = model_width
            post_para.ori_height = self.codec_para.height
            post_para.ori_width = self.codec_para.width
            post_para.score_threshold = 0.25
            post_para.nms_threshold = 0.45
            post_para.nms_top_k = 500
            post_para.is_pad_resize = 1
            post_para.output_tensor = apu_data_ptr
            post_para.class_num = self.label_list.labelNum
            post_para.labelList = self.label_list
            post_para.boxesInfo = boxes_info_ptr
            post_para.anchorSize = 3

            yolox_data = ctypes.string_at(
                ctypes.addressof(post_para), ctypes.sizeof(post_para)
            )
            ret = sdk.lyn_plugin_run_async(
                plugin_stream,
                self.plugin1,
                "lynYoloxPostProcess",
                yolox_data,
                ctypes.sizeof(YoloxPostProcessInfo_t),
            )
            common.error_check(ret, "lyn_plugin_run_async")

            sdk.lyn_record_event(plugin_stream, plugin_event)
            sdk.lyn_stream_wait_event(post_stream, plugin_event)
            sdk.lyn_stream_add_callback(
                post_stream, self.detect_callback, [cb_data.frame, boxes_info]
            )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [ipe_buffer_pool, ipe_buffer]
            )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [apu_buffer_pool, apu_buffer]
            )
            common.print_frame_rate(f"{self.device_id}_{self.channel_id}")
            if cancel_flag.value:
                break

        sdk.lyn_synchronize_stream(ipe_stream)
        sdk.lyn_synchronize_stream(apu_stream)
        sdk.lyn_synchronize_stream(post_stream)
        sdk.lyn_synchronize_stream(plugin_stream)
        sdk.lyn_destroy_event(ipe_event)
        sdk.lyn_destroy_event(apu_event)
        sdk.lyn_destroy_event(plugin_event)
        sdk.lyn_destroy_stream(ipe_stream)
        sdk.lyn_destroy_stream(apu_stream)
        sdk.lyn_destroy_stream(post_stream)
        sdk.lyn_destroy_stream(plugin_stream)
        sdk.lyn_unload_model(detect_model)
        ipe_detect.destory()

    def recognize_ipe(
        self, ipe_stream, frame, ipe_out_data, model_width, model_height, xywh
    ):
        pass

    def track(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        osd_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        post_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        osd_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        track_model, ret = sdk.lyn_load_model(self.track_model_path)
        common.error_check(ret, "lyn_load_model")
        # 获取模型信息
        model_desc, ret = sdk.lyn_model_get_desc(track_model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = (
            model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        )
        apu_output_size = model_desc.outputDataLen
        apu_buffer_pool = bufferpool.buffer_pool(apu_output_size * batch_size, 5 * 20)
        ipe_buffer_pool = bufferpool.buffer_pool(
            ipe_output_size * self.MAX_BATCH, 5 * 20
        )
        ipe_track = IpeParamTrack(model_width, model_height)
        ipe_track.set_img_info(
            self.codec_para.width,
            self.codec_para.height,
            sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
        )
        eos = False
        
        thread_id = threading.get_native_id()
        thread_id_int64 = struct.pack('q', thread_id)
        thread_id_int64_data = thread_id_int64
        sdk.lyn_plugin_run_async(
                    osd_stream,
                    self.plugin2,
                    "lynCreateTracker",
                    thread_id_int64_data,
                    len(thread_id_int64_data),
                )
        
        while not eos:
            cb_data: detect_frame = self.__detect_result_queue.take()
            eos = cb_data.frame.eos
            if cb_data.reconnect:
                sdk.lyn_stream_add_callback(
                    post_stream, put_to_queue, [self.__recognize_result_queue, cb_data]
                )
                self.__detect_result_queue.clear()
                continue
            host_buf = sdk.lyn_numpy_to_ptr(cb_data.host_box_info)
            host_buf_c = pythonapi.PyCapsule_GetPointer(host_buf, None)
            c_box = ctypes.cast(host_buf_c, ctypes.POINTER(Box)).contents

            ipe_buffer = ipe_buffer_pool.pop()
            offset = 0
            for i in range(min(self.MAX_BATCH, c_box.boxesnum)):
                bbox_instance = c_box.boxes[i]
                xywh, is_valid = get_rect(
                    bbox_instance, self.vdec_out_info.width, self.vdec_out_info.height
                )
                if not is_valid:
                    print("invalid rect ")
                    continue
                ipe_buffer_seek = sdk.lyn_addr_seek(ipe_buffer, ipe_output_size * i)

                ipe_track.calc_param_box(
                    ipe_stream, cb_data.frame.data, ipe_buffer_seek, xywh
                )

                offset += 1

            del cb_data.host_box_info

            if offset > 0:
                sdk.lyn_record_event(ipe_stream, ipe_event)
                sdk.lyn_stream_wait_event(apu_stream, ipe_event)

                apu_buffer = apu_buffer_pool.pop()
                sdk.lyn_execute_model_async(
                    apu_stream,
                    track_model,
                    ipe_buffer,
                    apu_buffer,
                    batch_size,
                )

                sdk.lyn_record_event(apu_stream, apu_event)
                sdk.lyn_stream_wait_event(osd_stream, apu_event)

                apu_buffer_addr_ptr = pythonapi.PyCapsule_GetPointer(apu_buffer, None)
                boxes_info_ptr = pythonapi.PyCapsule_GetPointer(
                    cb_data.device_box_info, None
                )
                frame_data_ptr = pythonapi.PyCapsule_GetPointer(
                    cb_data.frame.data, None
                )

                target_box, ret = sdk.lyn_malloc(ctypes.sizeof(Box))
                common.error_check(ret)
                target_box_ptr = pythonapi.PyCapsule_GetPointer(target_box, None)

                deepsort_para = struct.pack(
                    "q1i2P2i3P",
                    thread_id,
                    apu_output_size * batch_size,
                    apu_buffer_addr_ptr,
                    boxes_info_ptr,
                    self.vdec_out_info.width,
                    self.vdec_out_info.height,
                    0,
                    frame_data_ptr,
                    target_box_ptr,
                )

                sdk.lyn_plugin_run_async(
                    osd_stream,
                    self.plugin2,
                    "lynDeepsort",
                    deepsort_para,
                    len(deepsort_para),
                )

                osd_para = struct.pack(
                    "1P3i1P4i",
                    target_box_ptr,
                    self.codec_para.width,
                    self.codec_para.height,
                    sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
                    frame_data_ptr,
                    24,
                    4,
                    2,
                    4,
                )

                common.error_check(
                    sdk.lyn_plugin_run_async(
                        osd_stream,
                        self.osd_plugin,
                        "lynDrawBoxAndText",
                        osd_para,
                        len(osd_para),
                    )
                )

                sdk.lyn_record_event(osd_stream, osd_event)
                sdk.lyn_stream_wait_event(post_stream, osd_event)
                sdk.lyn_stream_add_callback(
                    post_stream, return_buffers, [apu_buffer_pool, [apu_buffer]]
                )
                sdk.lyn_stream_add_callback(
                    post_stream, free_device_memory, target_box
                )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffers, [ipe_buffer_pool, [ipe_buffer]]
            )
            sdk.lyn_stream_add_callback(
                post_stream, free_device_memory, cb_data.device_box_info
            )
            sdk.lyn_stream_add_callback(
                post_stream, put_to_queue, [self.__recognize_result_queue, cb_data]
            )
            if cancel_flag.value:
                break
            
        sdk.lyn_plugin_run_async(
                    osd_stream,
                    self.plugin2,
                    "lynDestroyTracker",
                    thread_id_int64_data,
                    len(thread_id_int64_data),
                )
        
        sdk.lyn_synchronize_stream(ipe_stream)
        sdk.lyn_synchronize_stream(apu_stream)
        sdk.lyn_synchronize_stream(osd_stream)
        sdk.lyn_synchronize_stream(post_stream)
        sdk.lyn_destroy_event(ipe_event)
        sdk.lyn_destroy_event(apu_event)
        sdk.lyn_destroy_event(osd_event)
        sdk.lyn_destroy_stream(ipe_stream)
        sdk.lyn_destroy_stream(apu_stream)
        sdk.lyn_destroy_stream(osd_stream)
        sdk.lyn_destroy_stream(post_stream)
        sdk.lyn_unload_model(track_model)
        ipe_track.destory()

    def generate_result(self, cancel_flag):
        """存储结果或者用opencv显示"""
        sdk.lyn_set_current_context(self.__ctx)
        if self.show_type == 1:
            encoder = Encoder(
                self.__ctx, self.frame_pool, self.vdec_out_info, self.output_path
            )
        elif self.show_type == 0:
            opencv_window = OpencvWindow(
                self.frame_pool, self.video_frame, self.vdec_out_info
            )
        eos = False
        while not eos:
            cb_data = self.__recognize_result_queue.take()
            frame = cb_data.frame
            if cb_data.reconnect:
                self.__recognize_result_queue.clear()
                break
            eos = frame.eos
            if self.show_type == 0:
                opencv_window.show(frame)
            elif self.show_type == 1:
                encoder.encode(frame)
            if cancel_flag.value:
                return
        if self.show_type == 1:
            del encoder

    def close(self):
        if self.__decode_send_thread.is_alive():
            self.__decode_send_thread.join()
        if self.__decode_recv_thread.is_alive():
            self.__decode_recv_thread.join()
        if self.__detect_thread.is_alive():
            self.__detect_thread.join()
        if self.__track_thread.is_alive():
            self.__track_thread.join()
        if self.__generate_result_thread.is_alive():
            self.__generate_result_thread.join()
        if self.__vdec_hdl != "":
            ret = sdk.lyn_vdec_close(self.__vdec_hdl)
            common.error_check(ret, "lyn_vdec_close")
        sdk.lyn_free(self.label_info_dev)
        sdk.lyn_plugin_unregister(self.plugin1)
        sdk.lyn_plugin_unregister(self.plugin2)
        sdk.lyn_plugin_unregister(self.osd_plugin)
        sdk.lyn_destroy_context(self.__ctx)
